#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio_ext.h>

#include "disk.h"

#define DISKFULL -1


int disk = 0;
int descriptor1;
int descriptor2;
int descriptor3;
int descriptor_parity;
int size_bytes;

int openDisk(char *filename, int nbytes) {
	size_bytes = nbytes;
	descriptor1 = createDescriptor("Disk1");
	descriptor2 = createDescriptor("Disk2");
	descriptor3 = createDescriptor("Disk3");
	descriptor_parity = createDescriptor("DiskParity");

	return 1;

/*	int i = 01101110011011110110111101100010;
	char *path = "disk1";
	int flags;
	//int flags = O_RDWR | O_CREAT | O_BINARY;
	//int flags = O_RDWR | O_CREAT;
	flags = O_RDWR;
	int descriptor = open(path, flags);
	FILE *fp;

	switch(errno) {
		//file doesn't exist so create binary file and then open it with open
		case ENOENT:
			fp = fopen(filename,"rb");
			fclose(fp);
			flags = O_RDWR;
			descriptor1 = open(path, flags);
			break;
		case EACCES:
			printf("Permission denied.\n");
			break;
		case EMFILE:
			printf("No file handle available. \n");
			break;
	}


	
	fclose(FILE *fp);
	FILE *fp = fopen(*filename, rb+);
	*//*
	return 1;
	*/
}

//creates a descriptor that openDisk can call
int createDescriptor(char *filename) {
		int flags;
		flags = O_RDWR;
		int descriptor = open(filename, flags);

		FILE *fp;
		int descriptorNew;

		switch(errno) {
			//file doesn't exist so create binary file and then open it with open
			case ENOENT:
				fp = fopen(filename,"wb");
				fclose(fp);
				descriptorNew = open(filename, flags);
				return descriptorNew;
				printf("lol\n");
				break;
			case EACCES:
				printf("Permission denied.\n");
				break;
			case EMFILE:
				printf("No file handle available. \n");
				break;
		}

		/*
		if(errno == ENOENT) {
			fp = fopen(filename,"wb");
			fclose(fp);
			descriptorNew = open(filename, flags);
			return descriptorNew;
			printf("lol\n");
		}
		else if(errno == EACCES) {
			printf("Permission denied.\n");
		}
		else if(errno == EMFILE) {
			printf("No file handle available. \n");
		}
		*/

	return descriptor;
}


int readBlock(int disk, int blocknr, int *block) {
	int blocksize = 1024;
	int position = (blocknr / 3) * blocksize;

	if(blocknr % 3 == 0) {
		//move file pointer
		//lseek(descriptor1, position, SEEK_SET);
		pread(descriptor1, block, blocksize, position);
		return 1;
	}
	else if(blocknr % 3 == 1) {
		//move file pointer
		//lseek(descriptor2, position, SEEK_SET);
		pread(descriptor2, block, blocksize, position);
		return 1;
	}
	else if(blocknr % 3 == 2) {
		//move file pointer
		//lseek(descriptor3, position, SEEK_SET);
		pread(descriptor3, block, blocksize, position);
		return 1;
	}
}


int writeBlock(int disk, int blocknr, int *block) {
	int blocksize = 1024;
	int *XOR = malloc(1024);
	int *contents1 = malloc(1024);
	int *contents2 = malloc(1024);
	int i = 0;

	if (blocknr * blocksize > size_bytes) {
		printf("The disk is full.\n");
		return DISKFULL;
	}

	//to find the position in the individual disk where the block should be written
	int position = (blocknr / 3) * blocksize;

	if (blocknr % 3 == 0) {
		pwrite(descriptor1, block, blocksize, position);


		//get the parity from the parity disk
		//lseek(descriptor2, position, SEEK_SET);
		pread(descriptor2, contents1, blocksize, position);

		//lseek(descriptor3, position, SEEK_SET);
		pread(descriptor3, contents2, blocksize, position);


		for (i = 0; i < 256; i++) {
			XOR[i] = block[i] ^ contents1[i] ^ contents2[i];
		}
		pwrite(descriptor_parity, XOR, blocksize, position);
		return 1;
	}
	else if (blocknr % 3 == 1) {
		pwrite(descriptor2, block, blocksize, position);

		//get the parity from the parity disk
		//lseek(descriptor1, position, SEEK_SET);
		pread(descriptor1, contents1, blocksize, position);

		//lseek(descriptor3, position, SEEK_SET);
		pread(descriptor3, contents2, blocksize, position);


		for (i = 0; i < 256; i++) {
			XOR[i] = block[i] ^ contents1[i] ^ contents2[i];
		}
		pwrite(descriptor_parity, XOR, blocksize, position);
		return 1;
	}
	else if (blocknr % 3 == 2) {
		pwrite(descriptor3, block, blocksize, position);

		//get the parity from the parity disk
		//lseek(descriptor1, position, SEEK_SET);
		pread(descriptor1, contents1, blocksize, position);

		//lseek(descriptor2, position, SEEK_SET);
		pread(descriptor2, contents2, blocksize, position);


		for (i = 0; i < 256; i++) {
			XOR[i] = block[i] ^ contents1[i] ^ contents2[i];
		}
		pwrite(descriptor_parity, XOR, blocksize, position);
		return 1;
	}
}


void syncDisk() {
	_flushlbf();
}


/*int main() {
	open_disk_test();
	write_test();
	read_test();
	test_parity();
	return 0;
}*/

//this function tests opening a disk and printing how big those disks are allowed to be
void open_disk_test() {

	int test_bytes = 300;
	FILE *filetest1 = fopen("Disk1", "r");
	FILE *filetest2 = fopen("Disk2", "r");
	FILE *filetest3 = fopen("Disk3", "r");
	FILE *filetestP = fopen("DiskParity", "r");
	openDisk("supdude", test_bytes);

	assert(test_bytes == size_bytes);
	assert(filetest1 != NULL);
	assert(filetest2 != NULL);
	assert(filetest3 != NULL);
	assert(filetestP != NULL);
}

//tests if the write function works
void write_test() {
	int *test_pointer = malloc(1024);
	int i = 0;

	for(i = 0; i < 256; i++) {
		test_pointer[i] = i * 200;
	}
	writeBlock(1, 1, test_pointer);
}


//writes information to a file and then reads it and compares if they are the same
void read_test() {
	int *test_pointerR = malloc(1024);
	int *test_pointerW = malloc(1024);

	int i = 0;
	for(i = 0; i < 256; i++) {
		test_pointerW[i] = i * 200;
	}
	writeBlock(1, 11, test_pointerW);
	readBlock(1, 11, test_pointerR);

	for (i = 0; i < 256; i++) {
		//printf("position %d\n", i);
		//printf("%d, %d\n", test_pointerW[i], test_pointerR[i]);
		assert(test_pointerW[i] == test_pointerR[i]);
	}


}

//checks to see if you can recreate a missing disk
void test_parity() {
	//put stuff in disk 1 and 2 in positions 9 & 10 since disk 3 position 11 already has something
	int *test_pointer1 = malloc(1024);
	int i = 0;

	for(i = 0; i < 256; i++) {
		test_pointer1[i] = i * 100;
	}
	writeBlock(1, 9, test_pointer1);


	int *test_pointer2 = malloc(1024);

	i=0;
	for(i = 0; i < 256; i++) {
		test_pointer2[i] = i * 300;
	}
	writeBlock(1, 10, test_pointer2);

	//read parity and XOR with disk 1 and disk 3 (assuming disk 2 is missing)
	int *test_pointer3 = malloc(1024);
	readBlock(1, 11, test_pointer3);

	int *test_pointer_one = malloc(1024);
	readBlock(1, 9, test_pointer_one);

	int *test_pointer_pairty = malloc(1024);
	int position = (11 / 3) * 1024;
	pread(descriptor_parity, test_pointer_pairty, 1024, position);

	i=0;
	int *XOR = malloc(1024);
	for (i = 0; i < 256; i++) {
				XOR[i] = test_pointer_one[i] ^ test_pointer3[i] ^ test_pointer_pairty[i];
		}

	//compare to see if it worked
	int *compare = malloc(1024);
	readBlock(1, 10, compare);

	i=0;
	for (i = 0; i < 256; i++) {
			//printf("re-create disk 2position %d\n", i);
			//printf("%d, %d\n", XOR[i], compare[i]);
			assert(XOR[i] == compare[i]);
	}

}
